#include "Sys_local.h"

enum Mode
{
    WM_Windowed,  //!< default
    WM_Fullscreen
};


class WindowWin32
{
public:
    WindowWin32() {
        window = 0;
        sysMsgTime = 0;

        mouseX = mouseY = 0.0f;
        mBtnLeft = mBtnMiddle = mBtnRight = false;
    }

    ~WindowWin32() {
        Close();
    }


    bool Open(const char title[], int width, int height ) {
#ifdef UNICODE
        // convert title to unicode
        wchar_t unicodeTitle[1024];
        MultiByteToWideChar( CP_ACP, 0, title, -1, unicodeTitle, sizeof(unicodeTitle) );
#endif

        // setup data
        this->width = width;
        this->height = height;

        // defaults
        run = true;
        window = NULL;
        systemMenu = NULL;
        active = false;
        centered = false;
        zoomLevel = ZOOM_ORIGINAL;
        mode = WM_Windowed;

        // get handle to system arrow cursor
        arrowCursor = LoadCursor( NULL, IDC_ARROW );

        // create null cursor so we can hide it reliably

        int cursorAnd = 0xFFFFFFFF;
        int cursorXor = 0;

        nullCursor = CreateCursor( NULL, 0, 0, 1, 1, &cursorAnd, &cursorXor );

        // setup window class

        HINSTANCE instance = GetModuleHandle(0);

        WNDCLASSEX windowClass;
        windowClass.cbSize = sizeof(WNDCLASSEX);
        windowClass.style = 0;
        windowClass.lpfnWndProc = &StaticWindowProc;
        windowClass.cbClsExtra = 0;
        windowClass.cbWndExtra = 0;
        windowClass.hInstance = instance;
        windowClass.hIcon = LoadIcon( instance, TEXT("DisplayIcon") );
        windowClass.hCursor = LoadCursor( NULL, IDC_ARROW );	// must set
        windowClass.hbrBackground = NULL;
        windowClass.lpszMenuName = NULL;
        windowClass.hIconSm = NULL;
#ifdef UNICODE
        windowClass.lpszClassName = unicodeTitle;
#else
        windowClass.lpszClassName = title;
#endif

#ifdef UNICODE
        UnregisterClass( unicodeTitle, instance );
#else
        UnregisterClass( title, instance );
#endif

        if ( !RegisterClassEx( &windowClass ) ) {
            window = 0;
            return false;
        }

        // create window
        DWORD style = WS_OVERLAPPEDWINDOW;
        RECT clientSize;
        clientSize.top = 0;
        clientSize.left = 0;
        clientSize.right = width;
        clientSize.bottom = height;

        AdjustWindowRect(&clientSize, style, FALSE);

        int realWidth = clientSize.right - clientSize.left;
        int realHeight = clientSize.bottom - clientSize.top;

        int windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
        int windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

#ifdef UNICODE
        window = CreateWindow( unicodeTitle, unicodeTitle, WS_OVERLAPPEDWINDOW, windowLeft, windowTop, realWidth, realHeight, NULL, NULL, instance, NULL );
#else
        window = CreateWindow( title, title, WS_OVERLAPPEDWINDOW, windowLeft, windowTop, realWidth, realHeight, NULL, NULL, instance, NULL );
#endif

        if ( !window )
            return false;

#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4244 )		// conversion from 'LONG_PTR' to 'LONG', possible loss of data (stupid win32 api! =p)
#endif

        SetWindowLongPtr( window, GWLP_USERDATA, (LONG_PTR) this );

#ifdef _MSC_VER
#pragma warning( pop )
#endif

        // setup system menu

        UpdateSystemMenu();

        return true;
    }

    void Close() {
        DestroyWindow( window );
        window = NULL;

        DestroyCursor( nullCursor );
        nullCursor = NULL;

    }

    // show the window (it is initially hidden)
    void Show() {
        ShowWindow( window, SW_SHOW );
    }

    // hide the window
    void Hide() {
        ShowWindow( window, SW_HIDE );
    }

    // check if window is visible?
    bool Visible() const {
        return IsWindowVisible( window ) != 0;
    }

    // put window in fullscreen mode
    void Fullscreen(int width, int height) {
        // 1. hide window
        // 2. hide mouse cursor
        // 3. popup window style
        // 4. move window to cover display entirely
        // 5. show window
        // 6. update system menu

        this->width = width;
        this->height = height;

        Hide();

        SetCursor( nullCursor );

        SetWindowLongPtr( window, GWL_STYLE, WS_POPUPWINDOW );	

        int w = GetSystemMetrics( SM_CXSCREEN );
        int h = GetSystemMetrics( SM_CYSCREEN );

        if ( width > w )
            w = width;

        if ( height > h )
            h = height;

        SetWindowPos( window, 0, 0, 0, w, h, SWP_NOZORDER );

        Show();

        mode = WM_Fullscreen;
        UpdateSystemMenu();

    }

    // put window in windowed mode
    void Windowed( int width, int height ) {
        // 1. hide window
        // 2. overlapped window style
        // 3. adjust window rect
        // 4. center window
        // 5. show mouse cursor
        // 6. update system menu

        this->width = width;
        this->height = height;

        Hide();

        SetWindowLongPtr( window, GWL_STYLE, WS_OVERLAPPEDWINDOW );

        RECT rect;
        GetWindowRect( window, &rect );
        AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, 0 );
        SetWindowPos( window, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER );

        Center();

        SetCursor( arrowCursor );

        mode = WM_Windowed;
        UpdateSystemMenu();

    }

    void CursorShow(bool v =true) {
        if(v)
            SetCursor( arrowCursor );
        else
            SetCursor( nullCursor );
    }

    void Center() {
        RECT rect;
        GetWindowRect( window, &rect );

        const int width = rect.right - rect.left;
        const int height = rect.bottom - rect.top;

        int x = ( GetSystemMetrics(SM_CXSCREEN) - width ) >> 1;
        int y = ( GetSystemMetrics(SM_CYSCREEN) - height ) >> 1;

        if ( x < 0 )
            x = 0;

        if ( y < 0 )
            y = 0;

        SetWindowPos( window, 0, x, y, width, height, SWP_NOZORDER );

        centered = true;

        UpdateSystemMenu();
    }

    // zoom window
    void Zoom( float scale ) {
        // get current window rect and calculate current window center

        RECT rect;
        GetWindowRect( window, &rect );

        const int cx = ( rect.left + rect.right ) / 2;
        const int cy = ( rect.top + rect.bottom ) / 2;

        // calculate window rect with origin (0,0)

        rect.left = 0;
        rect.top = 0;
        rect.right = rect.left + (int) ( width * scale );
        rect.bottom = rect.top + (int) ( height * scale );

        // adjust window rect then make adjust origin back to (0,0)

        AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, 0 );

        if ( rect.left < 0 )
        {
            rect.right -= rect.left;
            rect.left = 0;
        }

        if ( rect.top < 0 )
        {
            rect.bottom -= rect.top;
            rect.top = 0;
        }

        // center zoomed window around previous window center

        const int dx = cx - ( rect.right - rect.left ) / 2;
        const int dy = cy - ( rect.bottom - rect.top ) / 2;

        rect.left += dx;
        rect.right += dx;
        rect.top += dy;
        rect.bottom += dy;

        // check that the newly centered window position is origin (0,0) or larger. no negative origin values allowed

        if ( rect.left < 0 )
        {
            rect.right -= rect.left;
            rect.left = 0;
        }

        if ( rect.top < 0 )
        {
            rect.bottom -= rect.top;
            rect.top = 0;
        }

        // finally set the zoomed window position

        SetWindowPos( window, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER );

        // detect what zoom level we are at and update system menu

        if ( scale == 1.0f )
            zoomLevel = ZOOM_ORIGINAL;
        else if ( scale == 2.0f )
            zoomLevel = ZOOM_2X;
        else if ( scale == 4.0f )
            zoomLevel = ZOOM_4X;
        else if ( scale == 8.0f )
            zoomLevel = ZOOM_8X;
        else
            zoomLevel = ZOOM_RESIZED;
    }

    bool IsRun() const {
        return run;
    }

    // window update pumps the message queue
    void MessageProcess() {
        // hide mouse cursor if fullscreen

        if ( mode == WM_Fullscreen && active )
            SetCursor( nullCursor );

        if(!window) // invalid
            return;

        // message pump
        MSG message;
        //bool nextUpdate = true;
        while ( PeekMessage( &message, window, 0, 0, PM_REMOVE ) )
        {
            if(message.message == WM_QUIT)
            {
                run = false;
                break;
            }

            sysMsgTime = message.time;

            TranslateMessage( &message );
            DispatchMessage( &message );
        }
    }

    // get the window handle.
    // null if the window failed to initialize.
    HWND GetHandle() const {
        return window;
    }

    int GetWidth() const { return width; }
    int GetHeight() const { return height; }
    bool GetActive() const { return active; }

    Mode GetOutputMode() const {
        return mode;
    }

    // title management
    void Title( const char title[] ) {
        if ( !window )
            return;

#ifdef UNICODE
        wchar_t unicodeTitle[1024];
        MultiByteToWideChar( CP_ACP, 0, title, -1, unicodeTitle, sizeof(unicodeTitle) );
        SetWindowText( window, unicodeTitle );
#else
        SetWindowText( window, title );
#endif

    }

protected:

    static LRESULT CALLBACK StaticWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
        LONG_PTR extra = GetWindowLongPtr( hWnd, GWLP_USERDATA );

        if (!extra) 
            return DefWindowProc( hWnd, uMsg, wParam, lParam );

        WindowWin32 * window = (WindowWin32*) extra;
        return window->WindowProc( hWnd, uMsg, wParam, lParam );

    }

    LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
        int key;

        switch ( uMsg )
        {
        case WM_ACTIVATE:
            active = wParam != WA_INACTIVE;
            break;

        case WM_PAINT:
            break;

        case WM_SIZING:
            {
                RECT rect;
                ::GetClientRect(hWnd,&rect);
                width = rect.right - rect.left;
                height = rect.bottom - rect.top;

                zoomLevel = ZOOM_RESIZED;

            } break;
        case WM_SIZE:
            {
                UpdateSystemMenu();
                RECT rect;
                ::GetClientRect(hWnd,&rect);
                width = rect.right - rect.left;
                height = rect.bottom - rect.top;

            }
            break;
        case WM_CLOSE:
            run = false;
            PostQuitMessage(0);
            break;

        case WM_SETCURSOR:
            if ( LOWORD( lParam ) == HTCLIENT )
            {
                if ( mode == WM_Fullscreen )
                    SetCursor( nullCursor );
                else
                    SetCursor( arrowCursor );
            }
            else
                return DefWindowProc( hWnd, uMsg, wParam, lParam );
            break;

        case WM_SYSCOMMAND:
            {
                switch ( wParam )
                {
                case MENU_CENTER:
                    Center();
                    UpdateSystemMenu();
                    break;

                case MENU_ZOOM_ORIGINAL:
                    Zoom( 1.0f );
                    UpdateSystemMenu();
                    break;

                case MENU_ZOOM_2X:
                    Zoom( 2.0f );
                    UpdateSystemMenu();
                    break;

                case MENU_ZOOM_4X:
                    Zoom( 4.0f );
                    UpdateSystemMenu();
                    break;

                case MENU_ZOOM_8X:
                    Zoom( 8.0f );
                    UpdateSystemMenu();
                    break;

                case MENU_WINDOWED:
                    //context->windowed();
                    break;

                case MENU_FULLSCREEN:
                    //context->fullscreen();
                    break;

                default:
                    return DefWindowProc( hWnd, uMsg, wParam, lParam );
                }
            }
            break;

        case WM_MOVING:
            {
                if ( centered )
                {
                    centered = false;
                    UpdateSystemMenu();
                }
            }
            break;

        case WM_CHAR:
            {
                key = (int)wParam;
                Sys_QueEvent( sysMsgTime, SE_CHAR, translate[key].keyc, 1, 1, 0, NULL );
            }
            break;
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
            {
                key = (int)wParam;

                if( !translate[key].down ) {
                    translate[key].down = true;
                    Sys_QueEvent( sysMsgTime, SE_KEY, translate[key].keyc, true, 0, 0, NULL );

                }

            }
            break;

        case WM_KEYUP:
        case WM_SYSKEYUP:
            {
                key = (int)wParam;
                translate[key].down = false;

                Sys_QueEvent( sysMsgTime, SE_KEY, translate[key].keyc, false, 0, 0, NULL );
            }
            break;
        case WM_LBUTTONDOWN:
            {
                if ( !( mBtnLeft | mBtnRight | mBtnMiddle ) )
                    SetCapture( hWnd );
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                mouseX = (float) GET_X_LPARAM( lParam ) * widthRatio;
                mouseY = (float) GET_Y_LPARAM( lParam ) * heightRatio;
                mBtnLeft = true;

                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_ACTION1, true, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, mouseX, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, mouseY, 0, 0, NULL );
            }
            break;

        case WM_MBUTTONDOWN:
            {
                if ( !( mBtnLeft | mBtnRight | mBtnMiddle ) )
                    SetCapture( hWnd );
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                mouseX = (float) GET_X_LPARAM( lParam ) * widthRatio;
                mouseY = (float) GET_Y_LPARAM( lParam ) * heightRatio;
                mBtnMiddle = true;
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_ACTION3, true, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, mouseX, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, mouseY, 0, 0, NULL );
            }
            break;

        case WM_RBUTTONDOWN:
            {
                if ( !( mBtnLeft | mBtnRight | mBtnMiddle ) )
                    SetCapture( hWnd );
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                mouseX = (float) GET_X_LPARAM( lParam ) * widthRatio;
                mouseY = (float) GET_Y_LPARAM( lParam ) * heightRatio;
                mBtnRight = true;
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_ACTION2, true, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, mouseX, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, mouseY, 0, 0, NULL );
            }
            break;

        case WM_LBUTTONUP:
            {
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                mouseX = (float) GET_X_LPARAM( lParam ) * widthRatio;
                mouseY = (float) GET_Y_LPARAM( lParam ) * heightRatio;
                mBtnLeft = false;
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_ACTION1, false, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, mouseX, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, mouseY, 0, 0, NULL );
                if ( !( mBtnLeft | mBtnRight | mBtnMiddle ) )
                    ReleaseCapture();
            }
            break;

        case WM_MBUTTONUP:
            {
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                mouseX = (float) GET_X_LPARAM( lParam ) * widthRatio;
                mouseY = (float) GET_Y_LPARAM( lParam ) * heightRatio;
                mBtnMiddle = false;
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_ACTION3, false, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, mouseX, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, mouseY, 0, 0, NULL );
                if ( !( mBtnLeft | mBtnRight | mBtnMiddle ) )
                    ReleaseCapture();
            }
            break;

        case WM_RBUTTONUP:
            {
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                mouseX = (float) GET_X_LPARAM( lParam ) * widthRatio;
                mouseY = (float) GET_Y_LPARAM( lParam ) * heightRatio;
                mBtnRight = false;
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_ACTION2, true, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, mouseX, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, mouseY, 0, 0, NULL );
                if ( !( mBtnLeft | mBtnRight | mBtnMiddle ) )
                    ReleaseCapture();
            }
            break;

        case WM_MOUSEMOVE:
            {
                RECT rect;
                GetClientRect( window, &rect );
                const float widthRatio = (float) width / ( rect.right - rect.left );
                const float heightRatio = (float) height / ( rect.bottom - rect.top );
                const float x = (float) GET_X_LPARAM( lParam ) * widthRatio;
                const float y = (float) GET_Y_LPARAM( lParam ) * heightRatio;

                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAX, x, 0, 0, NULL );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAY, y, 0, 0, NULL );
            }
            break;
        case WM_MOUSEWHEEL:
            {
                int delta = GET_WHEEL_DELTA_WPARAM( wParam ) / WHEEL_DELTA;
                int wheelDown = delta < 0 ? true : false;
                delta = abs( delta );
                Sys_QueEvent( sysMsgTime, SE_MOUSE, M_DELTAZ, delta, wheelDown, 0, NULL );
            } break;



        case WM_WINDOWPOSCHANGED:
            {
                RECT rect;
                ::GetClientRect(hWnd,&rect);
                width = rect.right - rect.left;
                height = rect.bottom - rect.top;

                // resize
            }
            break;
        default: break;
        }

        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }

    // system menu item ids
    enum SystemMenuItems
    {
        MENU_SEPARATOR_A = 1,
        MENU_ZOOM_ORIGINAL,
        MENU_ZOOM_2X,
        MENU_ZOOM_4X,
        MENU_ZOOM_8X,
        MENU_SEPARATOR_B,
        MENU_WINDOWED,
        MENU_FULLSCREEN,
        MENU_SEPARATOR_C,
        MENU_CENTER
    };

    // current zoom level for window

    enum ZoomLevel
    {
        ZOOM_RESIZED,
        ZOOM_ORIGINAL,
        ZOOM_2X,
        ZOOM_4X,
        ZOOM_8X
    };

    // update addition system menu items

    void UpdateSystemMenu() {
        systemMenu = GetSystemMenu( window, FALSE );

        // remove additional items

        RemoveMenu( systemMenu, MENU_SEPARATOR_A, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_ZOOM_ORIGINAL, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_ZOOM_2X, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_ZOOM_4X, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_ZOOM_8X, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_SEPARATOR_B, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_FULLSCREEN, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_WINDOWED, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_SEPARATOR_C, MF_BYCOMMAND );
        RemoveMenu( systemMenu, MENU_CENTER, MF_BYCOMMAND );

        // rebuild menu

        bool windowed = mode == WM_Windowed;

        if ( windowed && !IsIconic(window) && !IsMaximized(window) )
        {
            AppendMenu( systemMenu, MF_SEPARATOR, MENU_SEPARATOR_A, TEXT("") );
            AppendMenu( systemMenu, MF_STRING, MENU_ZOOM_ORIGINAL, TEXT("Original Size") );

            const int desktopWidth = GetSystemMetrics( SM_CXSCREEN );
            const int desktopHeight = GetSystemMetrics( SM_CYSCREEN );

            if ( width*2 < desktopWidth && height*2 < desktopHeight )
                AppendMenu( systemMenu, MF_STRING, MENU_ZOOM_2X, TEXT("2x Zoom") );

            if ( width*4 < desktopWidth && height*4 < desktopHeight )
                AppendMenu( systemMenu, MF_STRING, MENU_ZOOM_4X, TEXT("4x Zoom") );

            if ( width*8 < desktopWidth && height*8 < desktopHeight )
                AppendMenu( systemMenu, MF_STRING, MENU_ZOOM_8X, TEXT("8x Zoom") );
        }

        AppendMenu( systemMenu, MF_SEPARATOR, MENU_SEPARATOR_B, TEXT("") );

        if ( !windowed )
            AppendMenu( systemMenu, MF_STRING, MENU_WINDOWED, TEXT("Windowed") );
        else
            AppendMenu( systemMenu, MF_STRING, MENU_FULLSCREEN, TEXT("Fullscreen") );

        if ( !centered && windowed )
        {
            AppendMenu( systemMenu, MF_SEPARATOR, MENU_SEPARATOR_C, TEXT("") );
            AppendMenu( systemMenu, MF_STRING, MENU_CENTER, TEXT("Center") );
        }
    }
private:
    HWND window;					// window handle
    HMENU systemMenu;				// system menu handle
    int width;						// natural window width
    int height;						// natural window height
    bool active;					// true if window is currently active
    bool run;

    Mode mode;						// current window mode (fullscreen or windowed)

    HCURSOR arrowCursor;			// handle to system arrow cursor (does not need to be freed)
    HCURSOR nullCursor;				// null cursor when we dont want to see it

    int				sysMsgTime;

    float   mouseX, mouseY;
    bool    mBtnLeft, mBtnMiddle, mBtnRight;

    bool centered;					// true if window is centered
    ZoomLevel zoomLevel;			// current zoom level
};

static WindowWin32 windowWin32;

void Sys_CreateWindow( const char title[], int width, int height )
{

    windowWin32.Open( title, width, height );
    windowWin32.CursorShow(true);
    windowWin32.Center();
    windowWin32.Show();
}

bool Sys_GetWindowRun()
{
    return windowWin32.IsRun();
}

bool Sys_GetWindowActive() {
    return windowWin32.GetActive();
}

void Sys_WindowFrame()
{
    windowWin32.MessageProcess();
}

HWND Sys_GetWndHandle()
{
    return windowWin32.GetHandle();
}